iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0

Kotlin 的屬性(Properties)看起來與 Java 的物件屬性相似,但它們實際上代表著不同的概念。

// Kotlin 屬性
var name: String? = null

// Java 欄位
String name = null;

儘管可以用相同的方式使用,把來資料記在物件上,但我們需要記住 Kotlin 的 properties 具有更多的能力

屬性欄位只是函式的組合


    var name: String? = null
        get() = field?.uppercase(Locale.getDefault())
        set(value) {
            if (!value.isNullOrBlank()) {
                field = value
            }
        }

在上例中,我們使用了 field 這個參數。這是指向讓我們在此屬性中資料的後置欄位的引用。這個 field 後置欄位是預設生成的,因為設定器 (setter) 和取值器的 (getter) 預設實現會使用它們。我們也可以實現不使用它們的自定義訪問器,這樣的情況下屬性根本不會有欄位值。例如,Kotlin 屬性可以僅使用取值器來定義唯讀屬性 val:

val fullName: String
get() = "$name $surname"

對於可讀寫的屬性 var,我們可以通過定義取值器和設定器來製作屬性。這種屬性被稱為衍生屬性,它們並不少見。它們是 Kotlin 中所有屬性預設封裝的主要原因。想像一下,你必須在你的類型中保存一個日期,你使用了來自 Java stdlib 的 Date。然後,由於某種原因,該類型不能再操作此類型的屬性。也許是因為序列化問題,或者是因為你將此類型提升到了一個共同的模組。問題是這個屬性已經在整個專案中被引用了。

使用 Kotlin,這不再是問題,因為你可以將數據移到另一個屬性 millis 中,並修改日期屬性不保存數據,而是包裝/解包那個其他屬性。


var date: Date
get() = Date(millis)
set(value) {
    millis = value.time
}

所以屬性其實不需要欄位。從概念上講,它們代表取值器(val 的取值器,var 的取值器和設定器)。這就是為什麼我們可以在介面定義屬性,其實是代表著取值器 + 設定器(var)

interface Person {
    val name: String
}

這意味著此介面承諾擁有一個取值器。我們還可以覆蓋屬性:


open class Supercomputer {
    open val theAnswer: Long = 42
}

class AppleComputer : Supercomputer() {
    override val theAnswer: Long = 1_800_275_2273
}

由於同樣的原因,我們可以委派屬性:


val db: Database by lazy { connectToDb() }

因為屬性本質上是函數,所以我們也可以製作擴展屬性:


val Context.preferences: SharedPreferences
get() = PreferenceManager
.getDefaultSharedPreferences(this)

val Context.inflater: LayoutInflater
get() = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

val Context.notificationManager: NotificationManager
get() = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

屬性的方法應是 Big O(1)

如你所見,屬性代表取值器,而不是欄位。這樣,它們可以代替某些函數,但我們應該小心我們如何使用它們。不應該使用屬性來代表高度運算的行為,如下例所示:

// 別這麼做!
val Tree<Int>.sum: Int
get() = when (this) {
    is Leaf -> value
    is Node -> left.sum + right.sum
}

此處 sum 屬性遍歷所有元素,因此它代表演算法行為。因此,此屬性會產生誤導:對於大集合,找到答案在計算上可能會很沉重,這對於取值器來說根本不符合預期。這不應該是一個屬性,這應該是一個函數:


fun Tree<Int>.sum(): Int = when (this) {
    is Leaf -> value
    is Node -> left.sum() + right.sum()
}

一般規則是,我們應該僅使用屬性它們來代表或設定狀態,不應該涉及其他邏輯

每日一推 (G)I-DLE

今天孩子們上了第二首 The first take - I DO

Yes


上一篇
D12: Kotlin 可讀性- 使用參數名與明確的型別
下一篇
D14: 可讀性 - 利用 Kotlin Scope Function 讓你可讀性與安全性的兼顧
系列文
讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言